using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace SalesPoint.Helpers
{
  public static class DataGridHelper
  {
    #region GetCell

    public static DataGridCell GetCell(DataGrid dataGrid, int row, int column)
    {
      var rowContainer = GetRow(dataGrid, row);
      if (rowContainer != null) {
        var presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);

        // try to get the cell but it may possibly be virtualized
        var cell = (DataGridCell) presenter.ItemContainerGenerator.ContainerFromIndex(column);
        if (cell == null) {
          // now try to bring into view and retreive the cell
          dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);

          cell = (DataGridCell) presenter.ItemContainerGenerator.ContainerFromIndex(column);
        }

        return cell;
      }

      return null;
    }

    #endregion GetCell

    #region GetRow

    /// <summary>
    /// Gets the DataGridRow based on the given index
    /// </summary>
    /// <param name="index">the index of the container to get</param>
    public static DataGridRow GetRow(DataGrid dataGrid, int index)
    {
      var row = (DataGridRow) dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
      if (row == null) {
        // may be virtualized, bring into view and try again
        dataGrid.ScrollIntoView(dataGrid.Items[index]);
        dataGrid.UpdateLayout();

        row = (DataGridRow) dataGrid.ItemContainerGenerator.ContainerFromIndex(index);
      }

      return row;
    }

    #endregion GetRow

    #region GetRowHeader

    /// <summary>
    /// Gets the DataGridRowHeader based on the row index.
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public static DataGridRowHeader GetRowHeader(DataGrid dataGrid, int index)
    {
      return GetRowHeader(GetRow(dataGrid, index));
    }

    /// <summary>
    /// Returns the DataGridRowHeader based on the given row.
    /// </summary>
    /// <param name="row">Uses reflection to access and return RowHeader</param>
    public static DataGridRowHeader GetRowHeader(DataGridRow row)
    {
      if (row != null) {
        return GetVisualChild<DataGridRowHeader>(row);
      }
      return null;
    }

    #endregion GetRowHeader

    #region GetColumnHeader

    public static DataGridColumnHeader GetColumnHeader(DataGrid dataGrid, int index)
    {
      var presenter = GetVisualChild<DataGridColumnHeadersPresenter>(dataGrid);

      if (presenter != null) {
        return (DataGridColumnHeader) presenter.ItemContainerGenerator.ContainerFromIndex(index);
      }

      return null;
    }

    #endregion GetColumnHeader                

    #region GetVisualChild

    public static T GetVisualChild<T>(Visual parent) where T : Visual
    {
      T child = default(T);

      int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
      for (int i = 0; i < numVisuals; i++) {
        var v = (Visual) VisualTreeHelper.GetChild(parent, i);
        child = v as T;
        if (child == null) {
          child = GetVisualChild<T>(v);
        }
        if (child != null) {
          break;
        }
      }

      return child;
    }

    public static T GetVisualChild<T>(Visual parent, int index) where T : Visual
    {
      T child = default(T);

      int encounter = 0;
      var queue = new Queue<Visual>();
      queue.Enqueue(parent);
      while (queue.Count > 0) {
        Visual v = queue.Dequeue();
        child = v as T;
        if (child != null) {
          if (encounter == index)
            break;
          encounter++;
        }
        else {
          int numVisuals = VisualTreeHelper.GetChildrenCount(v);
          for (int i = 0; i < numVisuals; i++) {
            queue.Enqueue((Visual) VisualTreeHelper.GetChild(v, i));
          }
        }
      }

      return child;
    }

    public static bool VisualChildExists(Visual parent, DependencyObject visualToFind)
    {
      var queue = new Queue<Visual>();
      queue.Enqueue(parent);
      while (queue.Count > 0) {
        Visual v = queue.Dequeue();
        DependencyObject child = v;
        if (child != null) {
          if (child == visualToFind)
            return true;
        }
        else {
          int numVisuals = VisualTreeHelper.GetChildrenCount(v);
          for (int i = 0; i < numVisuals; i++) {
            queue.Enqueue((Visual) VisualTreeHelper.GetChild(v, i));
          }
        }
      }

      return false;
    }

    #endregion GetVisualChild

    #region FindPartByName

    public static DependencyObject FindPartByName(DependencyObject ele, string name)
    {
      DependencyObject result;
      if (ele == null) {
        return null;
      }
      if (name.Equals(ele.GetValue(FrameworkElement.NameProperty))) {
        return ele;
      }

      int numVisuals = VisualTreeHelper.GetChildrenCount(ele);
      for (int i = 0; i < numVisuals; i++) {
        DependencyObject vis = VisualTreeHelper.GetChild(ele, i);
        if ((result = FindPartByName(vis, name)) != null) {
          return result;
        }
      }
      return null;
    }

    #endregion FindPartByName

    #region FindVisualParent

    public static T FindVisualParent<T>(UIElement element) where T : UIElement
    {
      UIElement parent = element;
      while (parent != null) {
        var correctlyTyped = parent as T;
        if (correctlyTyped != null) {
          return correctlyTyped;
        }

        parent = VisualTreeHelper.GetParent(parent) as UIElement;
      }

      return null;
    }

    #endregion FindVisualParent
  }
}